package com.jdrx.sw.sewage.service;

import com.github.pagehelper.PageHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jdrx.ocp.beans.entity.PointSetPO;
import com.jdrx.ocp.beans.entity.UserPO;
import com.jdrx.ocp.dao.PointSetDAO;
import com.jdrx.ocp.dao.UserDAO;
import com.jdrx.platform.commons.rest.exception.BizException;
import com.jdrx.sw.sewage.beans.dto.AddDTO;
import com.jdrx.sw.sewage.beans.dto.QueryDTO;
import com.jdrx.sw.sewage.beans.dto.StatisticsDTO;
import com.jdrx.sw.sewage.beans.dto.UpdateDTO;
import com.jdrx.sw.sewage.beans.entity.StationPO;
import com.jdrx.sw.sewage.beans.vo.*;
import com.jdrx.sw.sewage.beans.vo.base.PageVO;
import com.jdrx.sw.sewage.common.enums.EAlarmDict;
import com.jdrx.sw.sewage.common.enums.EDevOnLineStatus;
import com.jdrx.sw.sewage.common.enums.StationStatus;
import com.jdrx.sw.sewage.common.util.Asserts;
import com.jdrx.sw.sewage.common.util.NumberUtil;
import com.jdrx.sw.sewage.dao.DictDao;
import com.jdrx.sw.sewage.dao.ProcessunitDAO;
import com.jdrx.sw.sewage.dao.StationDAO;
import com.jdrx.sw.sewage.remote.TenantStationRemote;
import com.jdrx.sw.sewage.service.base.BaseServiceImpl;
import com.jdrx.sw.sewage.service.common.RedisService;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.jdrx.sw.sewage.common.constant.Constant.*;

@Service
public class StationServiceImpl extends BaseServiceImpl {
    public static final Logger logger = LoggerFactory.getLogger(StationServiceImpl.class);
    @Autowired
    private StationDAO stationDAO;
    @Autowired
    private RedisService redisService;
    @Autowired
    private AlarmServiceImpl alarmServiceImpl;
    @Autowired
    private DictServiceImpl dictServiceImpl;
    @Autowired
    private AlarmDataHandleServiceImpl AlarmDataHandleServiceImpl;
    @Autowired
    private TenantStationRemote tenantStationRemote;
    @Autowired
    private ProcessunitDAO processunitDAO;
    @Autowired
    private DictDao dictDao;
    @Autowired
    private UserDAO userDAO;
    @Autowired
    private PointSetDAO pointSetDAO;
    @Value("${aioFlag}")
    private String aioFlag;

    public PageVO<StationPO> pageList(QueryDTO dto) {
        if (dto.getStationIds() == null || dto.getStationIds().size() <= 0)
            return PageVO.toPageVO(Lists.newArrayList());
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        StationPO po = new StationPO();
        BeanUtils.copyProperties(dto, po);
        return PageVO.toPageVO(stationDAO.list(po));
    }

    public StationStatisticsVO statistics(StatisticsDTO dto) {
        return stationDAO.statistics(dto.getName(), dto.getStationIds());
    }

    @Transactional(rollbackFor = Exception.class)
    public void add(AddDTO dto) throws BizException {
        StationPO po = new StationPO();
        BeanUtils.copyProperties(dto, po);
        Map<String, Object> parmas = Maps.newHashMap();
        parmas.put(VALUE, EAlarmDict.OUTOFCONTACT.getKey());
        parmas.put(CLASSIFYKEY, ALARMCLASSIFY);
        List<DictVO> byParams = dictServiceImpl.findByParams(parmas);
        po.setAlarmType(byParams.get(0).getId().intValue());
        stationDAO.insert(po);
    }

    @Transactional(rollbackFor = Exception.class)
    public void update(UpdateDTO dto) throws Exception {
        StationVO stationById = stationDAO.findStationById(dto.getId());
        if (StationStatus.ABNORMAL.getKey() != stationById.getUseStatus() && StationStatus.ABNORMAL.getKey() == dto.getStatus())
            AlarmDataHandleServiceImpl.stationArrearsOutfcontact(null, stationById);

        if (StationStatus.ABNORMAL.getKey() == stationById.getUseStatus() && StationStatus.ABNORMAL.getKey() != dto.getStatus())
            AlarmDataHandleServiceImpl.sysCloseStationArrearsOutfcontact(null, stationById);

        StationPO po = new StationPO();
        BeanUtils.copyProperties(dto, po);
        stationDAO.update(po);

        // 信息同步至运营端
        if (StringUtils.isBlank(aioFlag)) {
            tenantStationRemote.update(dto);
        }
    }

    /**
     * 该接口供运营端远程调用，两边站点信息根据code同步
     *
     * @param dto
     * @throws BizException
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateByCode(UpdateDTO dto) throws BizException {
        StationVO stationById = stationDAO.findStationByCode(dto.getCode());
        if (StationStatus.ABNORMAL.getKey() != stationById.getUseStatus() && StationStatus.ABNORMAL.getKey() == dto.getStatus())
            AlarmDataHandleServiceImpl.stationArrearsOutfcontact(null, stationById);

        if (StationStatus.ABNORMAL.getKey() == stationById.getUseStatus() && StationStatus.ABNORMAL.getKey() != dto.getStatus())
            AlarmDataHandleServiceImpl.sysCloseStationArrearsOutfcontact(null, stationById);

        StationPO po = new StationPO();
        BeanUtils.copyProperties(dto, po);
        stationDAO.updateByCode(po);
    }

    public StationVO selectStationByNetNum(String netNum, String dataNode) {
        return stationDAO.selectStationByNetNum(netNum, dataNode);
    }

    public StationVO selectStationAlarmTypeById(Long stationId, String dataNode, Integer useStatus) {
        return stationDAO.selectStationAlarmTypeById(stationId, dataNode, useStatus);
    }

    public StationPO findById(Long id) {
        return stationDAO.findById(id);
    }

    public List<GISStationVO> selectGisStationList(List<Long> dutyUserStationIds, String dataNode) {
        if (dutyUserStationIds == null || dutyUserStationIds.size() == 0) {
            return Lists.newArrayList();
        }
        List<GISStationVO> gisStationVOS = stationDAO.selectGisStationList(dutyUserStationIds);

        gisStationVOS.forEach(item -> {
            try {
                Map<String, Object> redisMap = redisService.getMap(DEVICE_STATUS_KEY + dataNode + "_" + item.getId());
                Integer num = alarmServiceImpl.selectAlarmByStationId(item.getId());
                if (null != redisMap) {
                    if (EDevOnLineStatus.OFF_LINE.getKey() == MapUtils.getInteger(redisMap, "status"))
                        item.setStationStatus(StationStatus.OFF_LINE.getKey());

                    if ((EDevOnLineStatus.OFF_LINE.getKey() == MapUtils.getInteger(redisMap, "status") && 1 < num) ||
                            (EDevOnLineStatus.OFF_LINE.getKey() != MapUtils.getInteger(redisMap, "status") && 0 < num))
                        item.setStationStatus(StationStatus.ABNORMAL.getKey());
                } else if (0 < num) item.setStationStatus(StationStatus.ABNORMAL.getKey());
            } catch (BizException e) {
                logger.error("查询失败", e);
                e.printStackTrace();
            }
        });
        return gisStationVOS;
    }

    public List<StationNameVO> selectStationNameList(List<Long> dutyUserStationIds) {
        if (dutyUserStationIds == null || dutyUserStationIds.size() <= 0) return Lists.newArrayList();
        return stationDAO.selectStationNameList(dutyUserStationIds);
    }


    public GISStationInfoVO selectGisStationInfo(Long stationId) throws BizException {
        Asserts.notNull(stationId, "站点id不能为空");
        GISStationInfoVO gISStationInfoVO = stationDAO.selectStationById(stationId);
        Asserts.check(Objects.nonNull(gISStationInfoVO), "该站点id不存在");

        //获取累计流量、COD、TP、TN、NHN3H，先去数据库拿到redis - keys，再去redis取实时值，不要直接keys，redis会挂的！！！
        List<DeviceIotVO> deviceIotVOS = stationDAO.selectCSAndJSValue(stationId, CSQ, LJLL, COD, TP, TN, NH3N, SS, Y, PH);
        AtomicReference<Double> handleWaterTotalv = new AtomicReference<>(0d);
        for (DeviceIotVO item : deviceIotVOS) {
            if (item.getGatherDevPointValue().contains(LJLL))
                handleWaterTotalv.set(handleWaterTotalv.get() + item.getVal());
            if (item.getGatherDevPointValue().contains(COD)) gISStationInfoVO.setCOD(item.getVal());
            if (item.getGatherDevPointValue().contains(TP)) gISStationInfoVO.setTP(item.getVal());
            if (item.getGatherDevPointValue().contains(TN)) gISStationInfoVO.setTN(item.getVal());
            if (item.getGatherDevPointValue().contains(NH3N)) gISStationInfoVO.setNHN3H(item.getVal());
            if (item.getGatherDevPointValue().contains(SS)) gISStationInfoVO.setSS(item.getVal());
            if (item.getGatherDevPointValue().contains(PH)) gISStationInfoVO.setPH(item.getVal());
        }
        gISStationInfoVO.setHandleWaterTotal(handleWaterTotalv.get());
        return gISStationInfoVO;
    }

    public GISStationInfoAppVO selectGisStationInfoApp(Long stationId, Long uid) throws BizException {
        Asserts.notNull(stationId, "站点id不能为空");
        GISStationInfoVO gISStationInfoVO = stationDAO.selectStationById(stationId);
        Asserts.check(Objects.nonNull(gISStationInfoVO), "该站点id不存在");
        //new工艺单元-各点位属性值对象
        GISStationInfoAppVO gisStationInfoAppVO = new GISStationInfoAppVO();
        BeanUtils.copyProperties(gISStationInfoVO, gisStationInfoAppVO);
        //获取工艺单元信息
        List<ProcessunitVO> processunitVOS = processunitDAO.selectByStationId(stationId);
        //获取工艺单元显示配置
        List<PointSetPO> processunitSetPOS = pointSetDAO.listBy(ImmutableMap.of(
                "isShow", "0"
        ));
        if (processunitSetPOS != null && processunitSetPOS.size() > 0) {
            List<Long> ids = processunitSetPOS.stream().map(PointSetPO::getId).collect(Collectors.toList());
            String excludeProcessunitStr = StringUtils.join(ids, ",");
            //String excludeProcessunitStr = "158,170,172,184,197"; //排除工艺单元集合（加药等工艺单元，进水口二厂）
            //筛选工艺单元
            processunitVOS = processunitVOS.stream().filter(o -> !excludeProcessunitStr.contains(String.valueOf(o.getId()))).collect(Collectors.toList());
        }
        //获取点位显示配置（id，exclude_point进行筛选）
        List<PointSetPO> pointSetPOS = pointSetDAO.listBy(ImmutableMap.of(
                "isShow", "1"
        ));
        //String excludeCSQPointStr = "LJLL,SSLL,RCSLL,NO";//排除出水口点位
        //String excludeJSKPointStr = "WD,PH,SS,LJLL,SSLL,RJSLL,NJSLL,YJSLL,H2S"; //排除进水口点位（注意一厂二厂没办法用CSQ/JSK来区分，所以还是需要用ID来区分）
        //获取字典表信息
        //List<DictVO> dictVOS = dictDao.findByParams(ImmutableMap.of("classify_key", "pubGatrDevPoint"));
        //工艺单元list
        List<GISStationInfoProcessUnitVO> gisStationInfoProcessUnitVOS = new ArrayList<>();
        //出水总量
        AtomicReference<Double> handleWaterTotalv = new AtomicReference<>(0d);
        //进水总量
        AtomicReference<Double> inWaterTotalv = new AtomicReference<>(0d);
        //出水瞬时流量和
        AtomicReference<Double> handleWaterSSLTotalv = new AtomicReference<>(0d);
        //出水瞬时流量次数
        AtomicReference<Integer> handleWaterSSLLCountv = new AtomicReference<>(0);
        //进水瞬时流量和
        AtomicReference<Double> inWaterSSLLTotalv = new AtomicReference<>(0d);
        //进水瞬时流量次数
        AtomicReference<Integer> inWaterSSLLCountv = new AtomicReference<>(0);
        //获取所有点位信息
        List<DeviceIotAppVO> deviceIotAppVOS = stationDAO.selectCSAndJSValueApp(stationId, Y);
        for (ProcessunitVO processunitVO : processunitVOS) {  //站点-工艺单位遍历
            GISStationInfoProcessUnitVO gisStationInfoProcessUnitVO = new GISStationInfoProcessUnitVO();
            BeanUtils.copyProperties(processunitVO, gisStationInfoProcessUnitVO);
            List<GISStationInfoPointVO> gisStationInfoPointVOS = new ArrayList<>(); //站点-工艺单位-点位
            for (DeviceIotAppVO deviceIotAppVO : deviceIotAppVOS) { //站点所有点位遍历
                if (deviceIotAppVO.getVal() == null) {
                    deviceIotAppVO.setVal(0.0);
                }
                if (CSQ.equals(gisStationInfoProcessUnitVO.getValue())
                        && CSQ.equals(deviceIotAppVO.getGatherDevPointValueProcessUnit())
                        && LJLL.equals(deviceIotAppVO.getGatherDevPointValuePoint())) { //出水总量（多个点位会累加）
                    handleWaterTotalv.set(handleWaterTotalv.get() + deviceIotAppVO.getVal());
                }
                if (JSK.equals(gisStationInfoProcessUnitVO.getValue())
                        && JSK.equals(deviceIotAppVO.getGatherDevPointValueProcessUnit())
                        && LJLL.equals(deviceIotAppVO.getGatherDevPointValuePoint())) { //进水总量（多个点位会累加）
                    inWaterTotalv.set(inWaterTotalv.get() + deviceIotAppVO.getVal());
                }
                if (CSQ.equals(gisStationInfoProcessUnitVO.getValue())
                        && CSQ.equals(deviceIotAppVO.getGatherDevPointValueProcessUnit())
                        && SSLL.equals(deviceIotAppVO.getGatherDevPointValuePoint())) { //出水流量（先多个点位累加，后面要算平均值）
                    //gisStationInfoAppVO.setHandleWaterSpeed(deviceIotAppVO.getVal());
                    handleWaterSSLTotalv.set(handleWaterSSLTotalv.get() + deviceIotAppVO.getVal());
                    handleWaterSSLLCountv.set(handleWaterSSLLCountv.get() + 1);
                }
                if (JSK.equals(gisStationInfoProcessUnitVO.getValue())
                        && JSK.equals(deviceIotAppVO.getGatherDevPointValueProcessUnit())
                        && SSLL.equals(deviceIotAppVO.getGatherDevPointValuePoint())) { //进水流量（先多个点位累加，后面要算平均值）
                    //gisStationInfoAppVO.setInWaterSpeed(deviceIotAppVO.getVal());
                    inWaterSSLLTotalv.set(inWaterSSLLTotalv.get() + deviceIotAppVO.getVal());
                    inWaterSSLLCountv.set(inWaterSSLLCountv.get() + 1);
                }
                if (deviceIotAppVO.getGatherDevPointValueProcessUnit().equals(gisStationInfoProcessUnitVO.getValue())) {
                    //if (!((CSQ.equals(gisStationInfoProcessUnitVO.getValue()) && excludeCSQPointStr.contains(deviceIotAppVO.getGatherDevPointValuePoint())) ||
                    //(JSK.equals(gisStationInfoProcessUnitVO.getValue()) && excludeJSKPointStr.contains(deviceIotAppVO.getGatherDevPointValuePoint())))) {
                    //筛选排除进水口/出水口点位，后续可做配置功能，修改定义的excludeCSQPointStr和excludeJSKPointStr数据来源即可
                    Boolean flag = true;
                    for (PointSetPO pointSetPO : pointSetPOS) {
                        if (pointSetPO.getId().longValue() == processunitVO.getId().longValue()) {
                            String[] excludePoint = pointSetPO.getExcludePoint().split(",");
                            for (String str : excludePoint) {
                                if (str.equals(deviceIotAppVO.getGatherDevPointValuePoint())) {
                                    flag = false;
                                }
                            }
                        }
                    }
                    if (flag) {
                        GISStationInfoPointVO gisStationInfoPointVO = new GISStationInfoPointVO();
                        gisStationInfoPointVO.setDevPointValue(deviceIotAppVO.getGatherDevPointValue());
                        gisStationInfoPointVO.setValue(deviceIotAppVO.getGatherDevPointValuePoint());
                        gisStationInfoPointVO.setVal(deviceIotAppVO.getVal());
                        gisStationInfoPointVO.setParUnit(deviceIotAppVO.getParUnit());
                        gisStationInfoPointVO.setName(deviceIotAppVO.getDeviceName());
                        /*DictVO dictVO = dictVOS.stream().filter(down -> down.getValue().equals(gisStationInfoPointVO.getValue())).findAny().orElse(null);
                        if (dictVO != null) {
                            gisStationInfoPointVO.setName(dictVO.getDisName());
                        }*/
                        gisStationInfoPointVOS.add(gisStationInfoPointVO);
                    }
                    //}
                }
            }
            //去除已经匹配过的工艺单元
            deviceIotAppVOS = deviceIotAppVOS.stream().filter(o -> !o.getGatherDevPointValueProcessUnit().equals(gisStationInfoProcessUnitVO.getValue())).collect(Collectors.toList());
            //System.out.println(deviceIotAppVOS.size());
            //System.out.println(deviceIotAppVOS1.size());
            gisStationInfoProcessUnitVO.setGisStationInfoPointVOS(gisStationInfoPointVOS);
            gisStationInfoProcessUnitVOS.add(gisStationInfoProcessUnitVO);
        }
        gisStationInfoAppVO.setHandleWaterTotal(NumberUtil.round(handleWaterTotalv.get(), 2));
        gisStationInfoAppVO.setInWaterTotal(NumberUtil.round(inWaterTotalv.get(), 2));
        if (handleWaterSSLLCountv.get() != 0) {
            //求平均值
            //gisStationInfoAppVO.setHandleWaterSpeed(NumberUtil.round(handleWaterSSLTotalv.get() / handleWaterSSLLCountv.get(), 2));
            //求和
            gisStationInfoAppVO.setHandleWaterSpeed(NumberUtil.round(handleWaterSSLTotalv.get(), 2));
        }
        if (inWaterSSLLCountv.get() != 0) {
            //求平均值
            //gisStationInfoAppVO.setInWaterSpeed(NumberUtil.round(inWaterSSLLTotalv.get() / inWaterSSLLCountv.get(), 2));
            //求和
            gisStationInfoAppVO.setInWaterSpeed(NumberUtil.round(inWaterSSLLTotalv.get(), 2));
        }

        //封装出水/进水总量、出水/进水/流量
        GISStationInfoProcessUnitVO handleWaterTotalVO = new GISStationInfoProcessUnitVO(
                "出水总量",
                "CSQ_LJLL",
                "",
                gisStationInfoAppVO.getHandleWaterTotal(),
                "m³",
                null);
        gisStationInfoProcessUnitVOS.add(handleWaterTotalVO);
        GISStationInfoProcessUnitVO handleWaterSpeedTotalVO = new GISStationInfoProcessUnitVO(
                "出水流量",
                "CSQ_SSLL",
                "",
                gisStationInfoAppVO.getHandleWaterSpeed(),
                "m³/h",
                null);
        gisStationInfoProcessUnitVOS.add(handleWaterSpeedTotalVO);
        GISStationInfoProcessUnitVO inWaterTotalVO = new GISStationInfoProcessUnitVO(
                "进水总量",
                "JSK_LJLL",
                "",
                gisStationInfoAppVO.getInWaterTotal(),
                "m³",
                null);
        gisStationInfoProcessUnitVOS.add(inWaterTotalVO);
        GISStationInfoProcessUnitVO inWaterSpeedTotalVO = new GISStationInfoProcessUnitVO(
                "进水流量",
                "JSK_SSLL",
                "",
                gisStationInfoAppVO.getInWaterSpeed(),
                "m³/h",
                null);
        gisStationInfoProcessUnitVOS.add(inWaterSpeedTotalVO);

        gisStationInfoAppVO.setGisStationInfoProcessUnitVOS(gisStationInfoProcessUnitVOS);
        //数据卡片配置
        UserPO userPO = userDAO.getById(uid);
        if (userPO != null) {
            gisStationInfoAppVO.setFollowCards(userPO.getFollowCards());
        }

        return gisStationInfoAppVO;
    }


    public List<GroupDeviceDisplayVO> selectProcessunitByStationId(Long stationId, String dataNode, Long processunitId) {
        return stationDAO.selectProcessunitByStationId(stationId, dataNode, processunitId);
    }

    public List<DeviceDateDisplayVO> selectPoinValuesByStationId(Long stationId, String dataNode, String s, String p, Integer procShow) {
        return stationDAO.selectPoinValuesByStationId(stationId, dataNode, s, p, procShow);
    }

    public List<DeviceDateDisplayVO> selectPoinValuesByStationIdAndpId(Long stationId, Long processunitId, String dataNode, String key, String key2) {
        return stationDAO.selectPoinValuesByStationIdAndpId(stationId, processunitId, dataNode, key, key2);
    }

    public CommandDevVO selectPoinValuesByStationIdAndpIdAndgDid(Long stationId, Long processunitId, Long gatherDevPointId) {
        return stationDAO.selectPoinValuesByStationIdAndpIdAndgDid(stationId, processunitId, gatherDevPointId);
    }

    public List<DeviceDateDisplayVO> selectPoinPasByStationId(Long stationId, String key) {
        return stationDAO.selectPoinPasByStationId(stationId, key);
    }

    public List<DeviceDateDisplayVO> findPlcLogicByStationId(Long stationId, Integer key) {
        return stationDAO.findPlcLogicByStationId(stationId, key);
    }

    public LoginStationVO getLoginStation() {
        return stationDAO.getLoginStation();
    }
}